/*
 * Copyright (C) 2007-2010 Geometer Plus <contact@geometerplus.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

package org.geometerplus.fbreader.formats.oeb;

import org.geometerplus.fbreader.constants.XMLNamespace;
import org.geometerplus.fbreader.library.Book;
import org.geometerplus.zlibrary.core.filesystem.ZLFile;
import org.geometerplus.zlibrary.core.xml.ZLStringMap;
import org.geometerplus.zlibrary.core.xml.ZLXMLProcessor;
import org.geometerplus.zlibrary.core.xml.ZLXMLReaderAdapter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

class OEBMetaInfoReader extends ZLXMLReaderAdapter implements XMLNamespace {
    private final Book myBook;

    private String myDCMetadataTag = "dc-metadata";
    private String myMetadataTag = "metadata";
    private String myOpfMetadataTag = "metadata";
    private String myMetadataTagRealName;
    private String myTitleTag;
    private String myAuthorTag;
    private String mySubjectTag;
    private String myLanguageTag;
    private String myMetaTag = "meta";

    private String mySeriesTitle = "";
    private int mySeriesIndex = 0;

    private final ArrayList<String> myAuthorList = new ArrayList<String>();
    private final ArrayList<String> myAuthorList2 = new ArrayList<String>();

    OEBMetaInfoReader(Book book) {
        myBook = book;
        myBook.setTitle(null);
        myBook.setLanguage(null);
    }

    boolean readMetaInfo(ZLFile file) {
        myReadMetaData = false;
        myReadState = READ_NONE;

        if (!ZLXMLProcessor.read(this, file, 512)) {
            return false;
        }

        final ArrayList<String> authors = myAuthorList.isEmpty() ? myAuthorList2 : myAuthorList;
        for (String a : authors) {
            final int index = a.indexOf(',');
            if (index >= 0) {
                a = a.substring(index + 1).trim() + ' ' + a.substring(0, index).trim();
            } else {
                a = a.trim();
            }
            myBook.addAuthor(a);
        }

        return true;
    }

    private static final int READ_NONE = 0;
    private static final int READ_AUTHOR = 1;
    private static final int READ_AUTHOR2 = 2;
    private static final int READ_TITLE = 3;
    private static final int READ_SUBJECT = 4;
    private static final int READ_LANGUAGE = 5;
    private int myReadState;
    private boolean myReadMetaData;

    private final StringBuffer myBuffer = new StringBuffer();

    public boolean processNamespaces() {
        return true;
    }

    public void namespaceMapChangedHandler(HashMap<String, String> namespaceMap) {
        myTitleTag = null;
        myAuthorTag = null;
        mySubjectTag = null;
        myLanguageTag = null;
        myOpfMetadataTag = "metadata";
        for (Map.Entry<String, String> entry : namespaceMap.entrySet()) {
            final String id = entry.getValue();
            if (id.startsWith(DublinCorePrefix) || id.startsWith(DublinCoreLegacyPrefix)) {
                final String name = entry.getKey();
                myTitleTag = (name + ":title").intern();
                myAuthorTag = (name + ":creator").intern();
                mySubjectTag = (name + ":subject").intern();
                myLanguageTag = (name + ":language").intern();
            } else if (id.equals(OpenPackagingFormat)) {
                final String name = entry.getKey();
                myOpfMetadataTag = (name + ":metadata").intern();
            }
        }
    }

    public boolean startElementHandler(String tag, ZLStringMap attributes) {
        tag = tag.toLowerCase().intern();
        if (tag == myMetadataTag || tag == myDCMetadataTag || tag == myOpfMetadataTag) {
            myMetadataTagRealName = tag;
            myReadMetaData = true;
        } else if (myReadMetaData) {
            if (tag == myTitleTag) {
                myReadState = READ_TITLE;
            } else if (tag == myAuthorTag) {
                final String role = attributes.getValue("role");
                if (role == null) {
                    myReadState = READ_AUTHOR2;
                } else if (role.equals("aut")) {
                    myReadState = READ_AUTHOR;
                }
            } else if (tag == mySubjectTag) {
                myReadState = READ_SUBJECT;
            } else if (tag == myLanguageTag) {
                myReadState = READ_LANGUAGE;
            } else if (tag == myMetaTag) {
                if (attributes.getValue("name").equals("calibre:series")) {
                    mySeriesTitle = attributes.getValue("content");
                } else if (attributes.getValue("name").equals("calibre:series_index")) {
                    final String strIndex = attributes.getValue("content");
                    try {
                        mySeriesIndex = Integer.parseInt(strIndex);
                    } catch (NumberFormatException e) {
                    }
                }
            }
        }
        return false;
    }

    public void characterDataHandler(char[] data, int start, int len) {
        switch (myReadState) {
            case READ_NONE:
                break;
            case READ_AUTHOR:
            case READ_AUTHOR2:
            case READ_TITLE:
            case READ_SUBJECT:
            case READ_LANGUAGE:
                myBuffer.append(data, start, len);
                break;
        }
    }

    public boolean endElementHandler(String tag) {
        tag = tag.toLowerCase();
        if (tag.equals(myMetadataTagRealName)) {
            return true;
        }

        String bufferContent = myBuffer.toString().trim();
        if (bufferContent.length() != 0) {
            switch (myReadState) {
                case READ_TITLE:
                    myBook.setTitle(bufferContent);
                    break;
                case READ_AUTHOR:
                    myAuthorList.add(bufferContent);
                    break;
                case READ_AUTHOR2:
                    myAuthorList2.add(bufferContent);
                    break;
                case READ_SUBJECT:
                    myBook.addTag(bufferContent);
                    break;
                case READ_LANGUAGE: {
                    int index = bufferContent.indexOf('_');
                    if (index >= 0) {
                        bufferContent = bufferContent.substring(0, index);
                    }
                    index = bufferContent.indexOf('-');
                    if (index >= 0) {
                        bufferContent = bufferContent.substring(0, index);
                    }
                    myBook.setLanguage("cz".equals(bufferContent) ? "cs" : bufferContent);
                }
                break;
            }
        } else {
            if (tag.equals(myMetaTag)) {
                if (!mySeriesTitle.equals("") && mySeriesIndex > 0) {
                    myBook.setSeriesInfo(mySeriesTitle, mySeriesIndex);
                }
            }
        }
        myBuffer.delete(0, myBuffer.length());
        myReadState = READ_NONE;
        return false;
    }
}
